<?php

namespace pmvc\log;

use DateTime;

class DefaultFormatter
	implements Formatter {

	const VERBOSE_LOG_FORMAT 	= "[{levelName} {time} {file}:{line} {class}{type}{function} {name}] {message}{newline}";
	const BASIC_LOG_FORMAT		= "[{levelName} {time} {name}] {message}{newline}";
	const TIME_FORMAT			= "Y-m-d h:m:s";

	private static $levelNames = Array(
		Logger::TRACE	=> "TRACE",
		Logger::FINE	=> "FINE",
		Logger::DEBUG	=> "DEBUG",
		Logger::INFO	=> "INFO",
		Logger::WARN	=> "WARN",
		Logger::ERROR	=> "ERROR",
		Logger::FATAL	=> "FATAL"
	);

	private $logFormat;
	private $timeFormat;

	/**
	 * Creates the formatter.
	 * @param string $logFormat the log format
	 * @param string $timeFormat the time format
	 */
	public function __construct(
		$logFormat=self::BASIC_LOG_FORMAT, $timeFormat=self::TIME_FORMAT) {
		$this->logFormat 	= $logFormat;
		$this->timeFormat	= $timeFormat;
	}

	/**
	 * {@inheritDoc}
	 */
	public function format($level, DateTime $time, $name, $message) {

		// format level and time
		$formattedLevel = isset(self::$levelNames[$level]) ? self::$levelNames[$level] : "UNKNOWN";
		$formattedTime	= $time->format($this->timeFormat);

		// lazy loading of the back trace
		$backtrace = null;
		$bt = function($name)use(&$backtrace) {
			if (!isset($backtrace)) {
				$backtrace = DefaultFormatter::getDebugBacktrace();
			}
			return $backtrace[$name];
		};

		// build the log message and return it
		return preg_replace_callback('/\{(level|levelName|time|name|message|line|file|basefile|class|baseclass|type|function|newline)}/',
			function($matches)use($level, $formattedLevel, $formattedTime, $name, $message, $bt) {
				switch($matches[1]) {
					case "level"		: return $level;
					case "levelName"	: return $formattedLevel;
					case "time" 		: return $formattedTime;
					case "name" 		: return $name;
					case "message" 		: return $message;
					case "line" 		: return $bt("line");
					case "file" 		: return $bt("file");
					case "basefile"		: return basename($bt("file"));
					case "class" 		: return $bt("class");
					case "baseclass"	: return substr($bt("class"), strrpos($bt("class"), "\\")+1);
					case "type" 		: return $bt("type");
					case "function" 	: return $bt("function");
					case "newline" 		: return "\n";
					default				: return $matches[0];
				}
		}, $this->logFormat);
	}

	/**
	 * Method for loading the back trace.
	 * @return array a single element from the backtrace
	 */
	public static function getDebugBacktrace() {
		$trace= Array(
			"line"		=> -1,
			"file"		=> "unknown",
			"class"		=> "unknown",
			"type"		=> "unknown",
			"function"	=> "function"
		);
		if (function_exists("debug_backtrace")) {
			$traces = debug_backtrace();
			$count = count($traces);
			if ($count==1) {
				return $traces[0];
			}
			$curTrace 	= null;
			$lastTrace	= null;
			for ($i=1; $i<$count; $i++) {
				$curTrace 	= $traces[$i];
				$lastTrace	= $i>0 ? $traces[$i-1] : null;
				if (isset($curTrace["class"])
					&& strpos($curTrace["class"], 'pmvc\log')!==0) {
					$trace["line"] = isset($lastTrace["line"]) ? $lastTrace["line"] : null;
					$trace["file"] = isset($lastTrace["file"]) ? $lastTrace["file"] : null;
					break;
				}
			}
			$trace["class"] = isset($curTrace["class"]) ? $curTrace["class"] : "none";
			$trace["type"] = isset($curTrace["type"]) ? $curTrace["type"] : "";
			if (isset($curTrace["function"])) {
				$trace["function"] = $curTrace["function"];
			} else {
				$trace["function"] = "main";
			}
		}
		return $trace;
	}

}

?>